#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
/*
  当有一个生产者和多个消费者时候
  无名信号是线程同步就很麻烦了，这时候就使用条件变量同步

  条件变量：
    本质上也是一个临界资源，他维护了一个队列，当消费者线程想要执行时，先进入队列中
    等待生产者的唤醒。执行完生产者，再由生产者唤醒在队列中的消费者，这样就完成了生产者和消费者
    之间的同步关系。但是，多个消费者在进入休眠队列的过程是互斥的，所以，在消费者准备进入休眠队列时，需要使
    用互斥锁来进行互斥操作


API:
#include <pthread.h>

1、创建一个条件变量，只需定义一个pthread_cond_t类型的全局变量即可
    pthread_cond_t cond;

2、初始化条件变量
    pthread_cond_t cond = PTHREAD_COND_INITIALIZER;//静态初始化

    int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
    功能：初始化条件变量
    参数1：条件变量的起始地址
    参数2：条件变量的属性，一般填NULL
    返回值：成功返回0，失败返回一个错误码

3、消费者线程进入等待队列
    int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
    功能：将线程放入休眠等待队列，等待其他线程的唤醒
    参数1：条件变量的地址
    参数2：互斥锁，由于多个消费者线程进入等待队列时会产生竞态，为了解决竞态，需要使用一个互斥锁
           负责释放和获取锁，如果获取锁失败就继续阻塞在等待队列中
    返回值：成功返回0，失败返回错误码

4、生产者线程唤醒休眠队列中的任务
    int pthread_cond_broadcast(pthread_cond_t *cond);
    功能：唤醒条件变量维护的队列中的所有消费者线程
    参数：条件变量的地址
    返回值：成功返回0，失败返回错误码

    int pthread_cond_signal(pthread_cond_t *cond);
    功能：唤醒条件变量维护的队列中的第一个进入队列的消费者线程
    参数：条件变量的地址
    返回值：成功返回0，失败返回错误码

5、销毁条件变量
    int pthread_cond_destroy(pthread_cond_t *cond);
    功能：销毁一个条件变量
    参数：条件变量的地址
    返回值：成功返回0，失败返回错误码
*/

pthread_cond_t cond;   // 定义一个条件变量
pthread_mutex_t mutex; // 定义一个互斥锁

// 创建生产者线程
void *task1(void *arg)
{

    int num = 3;
    while (num--)
    {
        printf("%#x:生产了一辆特斯拉\n", pthread_self());
        // 唤醒一个消费者进行消费
        pthread_cond_signal(&cond);
        // pthread_cond_broadcast(&cond); // 唤醒所有消费者线程
        sleep(2);
    }

    // 退出线程
    pthread_exit(NULL);
}

// 创建消费者线程
void *task2(void *arg)
{

    pthread_mutex_lock(&mutex);       // 获取锁资源
    //一定要先加锁，因为条件变量cond本身也是临界资源
    pthread_cond_wait(&cond, &mutex); // 进入休眠队列，等待生产者的唤醒
    //如果条件不满足，释放锁资源，进入队列等待
    //条件满足时，在这条指令唤醒然后判断有无加锁，有锁权就执行，没有就继续阻塞在等待队列中

    printf("%#x:消费了一辆特斯拉，很开心\n", pthread_self());

    pthread_mutex_unlock(&mutex); // 释放锁资源
    pthread_exit(NULL);           // 退出线程
}

int main()
{
    // 初始化条件变量
    pthread_cond_init(&cond, NULL);
    // 初始化互斥锁
    pthread_mutex_init(&mutex, NULL);

    pthread_t tid1, tid2, tid3, tid4;
    pthread_create(&tid1, NULL, task1, NULL); // 创建生产者线程
    pthread_create(&tid2, NULL, task2, NULL); // 消费者1
    pthread_create(&tid3, NULL, task2, NULL); // 消费者2
    pthread_create(&tid4, NULL, task2, NULL); // 消费者3
    printf("tid1 = %#x, tid2 = %#x, tid3 = %#x, tid4 = %#x\n", tid1, tid2, tid3, tid4);
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    pthread_join(tid3, NULL);
    pthread_join(tid4, NULL);

    // 销毁条件变量
    pthread_cond_destroy(&cond);
    /// 销毁互斥锁
    pthread_mutex_destroy(&mutex);
    printf("条件变量和互斥锁销毁成功\n");
    return 0;
}